/* gpsd.h -- fundamental types and structures for the gpsd library
 *
 * Nothing in this file should be used by any client.  So safe to change
 * anything here without (directly) affecting the API or ABI.
 *
 * This file is Copyright 2017 by the GPSD project
 * SPDX-License-Identifier: BSD-2-clause
 */

#ifndef _GPSD_H_
#define _GPSD_H_

#include "compiler.h"   // Must be outside extern "C" for "atomic"

# ifdef __cplusplus
extern "C" {
# endif

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>    // for time_t

#include "gps.h"
#include "timespec.h"

/*
 * Constants for the VERSION response
 * 3.1: Base JSON version
 * 3.2: Added POLL command and response
 * 3.3: AIS app_id split into DAC and FID
 * 3.4: Timestamps change from seconds since Unix epoch to ISO8601.
 * 3.5: POLL subobject name changes: fixes -> tpv, skyview -> sky.
 *      DEVICE::activated becomes ISO8601 rather than real.
 * 3.6  VERSION, WATCH, and DEVICES from slave gpsds get "remote" attribute.
 * 3.7  PPS message added to repertoire. SDDBT water depth reported as
 *      negative altitude with Mode 3 set.
 * 3.8  AIS course member becomes float in scaled mode (bug fix).
 * 3.9  split24 flag added. Controlled-vocabulary fields are now always
 *      dumped in both numeric and string form, with the string being the
 *      value of a synthesized additional attribute with "_text" appended.
 *      (Thus, the 'scaled' flag no longer affects display of these fields.)
 *      PPS drift message ships nsec rather than msec.
 * 3.10 The obsolete tag field has been dropped from JSON.
 * 3.11 A precision field, log2 of the time source jitter, has been added
 *      to the PPS report.  See ntpshm.h for more details.
 * 3.12 OSC message added to repertoire.
 * 3.13 gnssid:svid added to SAT
 *      time added to ATT
 * 3.19 Added RAW message class.
 *      Add cfg_stage and cfg_step, for initialization
 *      Add oldfix2 for better oldfix
 *      Make subtype longer
 *      Add ubx.protver, ubx.last_msgid and more to gps_device_t.ubx
 *      MAX_PACKET_LENGTH 516 -> 9216
 *      Add stuff to gps_device_t.nmea for NMEA 4.1
 * 3.20
 *      Remove TIMEHINT_ENABLE.  It only worked when enabled.
 *      Remove NTP_ENABLE and NTPSHM_ENABLE.  It only worked when enabled.
 *      Change gps_type_t.min_cycle from double to timespec_t
 *      Change gps_device_t.last_time from double to timespec_t
 *      Change gps_lexer_t.start_time from timestamp_t to timespec_t
 *      Change gps_context_t.gps_tow from double to timespec_t
 *      Change gps_device_t.sor from timestamp_t to timespec_t
 *      Change gps_device_t.this_frac_time, last_frac_time to timespec_t
 *      Change nmea.subseconds from double to timespec_t
 *      Remove gpsd_gpstime_resolve()
 *      Changed order of gpsd_log() arguments.  Add GPSD_LOG().
 *      Remove gps_device_t.back_to_nmea.
 *      Add fixed_port_speed, fixed_port_framing to gps_context_t.
 *      change tsip.superpkt from bool to int.
 *      Add tsip  .machine_id, .hardware_code, .last_tow, last_chan_seen
 *      Split gps_device_t.subtype into subtype and subtype1
 * 3.21
 *      GPSD_PROTO_*_VERSION moved to gpsd_config.h
 *      Add gps_context_t.passive
 *      Add gps_context_t.batteryRTC
 * 3.21.1
 *      Add gps_device_t.ubx.last_protver
 *      Add gps_device_t last_word_gal and last_svid3_gal
 *      Add timespec ts_startCurrentBaud to gps_device_t
 *      ntrip_conn_* to NTRIP_CONN_*
 * 3.24
 *      add ntrip_parse_url(), ntrip_close()
 *      add host, stream_time, to ntrip_stream_t
 *      add shm_clock_unit and shm_pps_unit to device_t
 *      add VALID_UNIT()
 *      remove shm_clock and shm_pps from device_t
 *      add pkt_time to lexer_t
 *      add netlib_connectsock1()
 *      add nmea.gsx_more to gps_device_t
 *      add TSIPv1 stuff
 * 3.25
 *      add end_gsv_talker and gga_sats_used to gps_device_t.nmea
 */


// be sure to change BUILD_LEAPSECONDS as needed.
#define BUILD_CENTURY 2000
#define BUILD_LEAPSECONDS 18

// First, declarations for the packet layer...

/*
 * NMEA 3.01, Section 5.3 says the max sentence length shall be
 * 82 chars, including the leading $ and terminating \r\n.
 *
 * Some receivers (TN-200, GSW 2.3.2) emit oversized sentences.
 * The Trimble BX-960 receiver emits a 91-character GGA message.
 * The Skytraq S2525F8 which emits a 100-character PSTI message.
 * The current hog champion is the Skytraq PX1172RH which emits
 * a 103-character PSTI message.
 */
#define NMEA_MAX        110             // max length of NMEA sentence
#define NMEA_MAX_FLD    100             // max fields in an NMEA sentence
#define NMEA_BIG_BUF    (2*NMEA_MAX+1)  // longer than longest NMEA sentence


/*
 * UTC of second 0 of week 0 of the first rollover period of GPS time.
 * Used to compute UTC from GPS time. Also, the threshold value
 * under which system clock times are considered unreliable. Often,
 * embedded systems come up thinking it's early 1970 and the system
 * clock will report small positive values until the clock is set.  By
 * choosing this as the cutoff, we'll never reject historical GPS logs
 * that are actually valid.
 */
#define GPS_EPOCH       ((time_t)315964800)     // 6 Jan 1980 00:00:00 UTC

// time constant
#define SECS_PER_DAY    ((time_t)(60*60*24))    // seconds per day
#define SECS_PER_WEEK   (7*SECS_PER_DAY)        // seconds per week
#define GPS_ROLLOVER    (1024*SECS_PER_WEEK)    // rollover period

struct gpsd_errout_t {
    int debug;                          // lexer debug level
    void (*report)(const char *);       // reporting hook for lexer errors
    const char *label;
};


// factors for converting among confidence interval units
#define CEP50_SIGMA     1.18
#define DRMS_SIGMA      1.414
#define CEP95_SIGMA     2.45

// this is where we choose the confidence level to use in reports
#define GPSD_CONFIDENCE CEP95_SIGMA


struct gps_device_t;

struct gps_context_t {
    int valid;                          // member validity flags
#define LEAP_SECOND_VALID       0x01    // we have or don't need correction
#define GPS_TIME_VALID          0x02    // GPS week/tow is valid
#define CENTURY_VALID           0x04    // have received ZDA or 4-digit year
    struct gpsd_errout_t errout;        // debug verbosity level and hook
    bool readonly;                      // if true, never write to device
    bool passive;                       // if true, never autoconfigure device
    // if true, remove fix gate to time, for some RTC backed receivers.
    // DANGEROUS
    bool batteryRTC;
    char fixed_port_framing[4];         // Fixed port framing, if non-blank
    // DGPS status
    int fixcnt;                         // count of good fixes seen
    // timekeeping
    time_t start_time;                  // local time of daemon startup
    int leap_seconds;                   // Unix secs to UTC (GPS-UTC offset)
    unsigned short gps_week;            // GPS week, usually 10 bits
    timespec_t gps_tow;                 // GPS time of week
    int century;                        // for NMEA-only devices without ZDA
    int rollovers;                      // rollovers since start of run
    int leap_notify;                    // notification state from subframe
#define LEAP_NOWARNING  0x0     // normal, no leap second warning
#define LEAP_ADDSECOND  0x1     // last minute of day has 60 seconds
#define LEAP_DELSECOND  0x2     // last minute of day has 59 seconds
#define LEAP_NOTINSYNC  0x3     // overload, clock is free running
};

// typedef enum {ANY, GPS, RTCM2, RTCM3, AIS} gnss_type;
typedef enum {
    event_wakeup,
    event_triggermatch,
    event_identified,
    event_configure,
    event_driver_switch,
    event_deactivate,
    event_reactivate,
} event_t;


#define INTERNAL_SET(n) ((gps_mask_t)(1llu<<(SET_HIGH_BIT+(n))))
#define RAW_IS          INTERNAL_SET(1)         // raw pseudoranges available
#define USED_IS         INTERNAL_SET(2)         // sat-used count available
#define DRIVER_IS       INTERNAL_SET(3)         // driver type identified
#define CLEAR_IS        INTERNAL_SET(4)         // starts a reporting cycle
#define REPORT_IS       INTERNAL_SET(5)         // ends a reporting cycle
#define NODATA_IS       INTERNAL_SET(6)         // no data read from fd
#define NTPTIME_IS      INTERNAL_SET(7)         // precision time is available
#define PERR_IS         INTERNAL_SET(8)         // PDOP set
#define PASSTHROUGH_IS  INTERNAL_SET(9)         // passthrough mode
#define EOF_IS          INTERNAL_SET(10)        // synthetic EOF
#define GOODTIME_IS     INTERNAL_SET(11)        // time good even if no pos fix
#define DATA_IS ~(ONLINE_SET|PACKET_SET|CLEAR_IS|REPORT_IS)


// session object, encapsulates all global state
struct gps_device_t {
    struct gps_data_t gpsdata;
    struct gps_context_t        *context;
    // firmware version or subtype ID, 96 too small for ZED-F9
    char subtype[128];
    char subtype1[128];
    /*
     * msgbuf needs to hold the hex decode of inbuffer
     * so msgbuf must be 2x the size of inbuffer
     */
    int observed;                       // which packet type`s have we seen?
    bool cycle_end_reliable;            // does driver signal REPORT_MASK
    struct gps_fix_t newdata;           // where drivers put their data
    struct {
        unsigned short sats_used[MAXCHANNELS];
        int part, await;                // for tracking GSV parts
        struct tm date;                 // date part of last sentence time
        timespec_t subseconds;          // subsec part of last sentence time
        // pointers to each field in fieldcopy.  Each is NUL terminated.
        char *field[NMEA_MAX_FLD];
        unsigned char fieldcopy[NMEA_MAX+1]; // a copy of the current sentence
        // detect receivers that ship GGA with non-advancing timestamp
        bool latch_mode;
        char last_gga_timestamp[16];
        char last_gga_talker;
        // GSV stuff
        bool seen_bdgsv;                        // BeiDou GSV
        bool seen_gagsv;                        // Galileo GSV
        bool seen_gigsv;                        // IRNSS GSV
        bool seen_glgsv;                        // GLONASS GSV
        bool seen_gngsv;                        // GNSS GSV
        bool seen_gpgsv;                        // GPS GSV
        bool seen_qzgsv;                        // QZSS GSV
        char last_gsv_talker;
        char end_gsv_talker;
        unsigned char last_gsv_sigid;           // NMEA 4.1
        // GSA stuff
        bool seen_bdgsa;                        // BeiDou GSA
        bool seen_gagsa;                        // Galileo GSA
        bool seen_gigsa;                        // IRNSS GSA
        bool seen_glgsa;                        // GLONASS GSA
        bool seen_gpgsa;                        // GPS GSA
        bool seen_gngsa;                        // GNSS GSA
        bool seen_qzgsa;                        // QZSS GSA
        char last_gsa_talker;
        /*
         * State for the cycle-tracking machinery.
         * The reason these timestamps are separate from the
         * general sentence timestamps is that we can
         * use the minutes and seconds part of a sentence
         * with an incomplete timestamp (like GGA) for
         * end-cycle recognition, even if we don't have a previous
         * RMC or ZDA that lets us get full time from it.
         */
        timespec_t this_frac_time, last_frac_time;
        bool latch_frac_time;
        int lasttag;              // index into nmea_phrase[]
        // 140 > number of used entries in nmea_phrase[]
#define NMEA_NUM 140
        // bit map into nmea_phrase[], +1 to shut up coverity
        bool cycle_enders[NMEA_NUM + 1];
        bool cycle_continue;
        bool gsx_more;         // more GSV or GSA to come.
        unsigned gga_sats_used;  // sats used from GGA, GNS or $PASHR
    } nmea;
};


// logging levels
#define LOG_ERROR       -1      // errors, display always
#define LOG_SHOUT       0       // not an error but we should always see it
#define LOG_WARN        1       // not errors but may indicate a problem
#define LOG_CLIENT      2       // log JSON reports to clients
#define LOG_INF         3       // key informative messages
#define LOG_PROG        4       // progress messages
#define LOG_IO          5       // IO to and from devices
#define LOG_DATA        6       // log data management messages
#define LOG_SPIN        7       // logging for catching spin bugs
#define LOG_RAW         8       // raw low-level I/O
#define LOG_RAW1        9       // rawer
#define LOG_RAW2       10       // rawest

#define ISGPS_ERRLEVEL_BASE     LOG_RAW

// gpsd library internal prototypes
extern void gpsd_time_init(struct gps_context_t *, time_t);
/**
 * @brief 解析多条NMEA句子
 * @param  buf              
 * @param  len              
 * @param  session          
 * @return int 
 * @note null
 */
int nmea_parse_mult(char *buf, int len, struct gps_device_t *session);
extern gps_mask_t nmea_parse(char *, struct gps_device_t *);
extern void nmea_add_checksum(char *);


extern timespec_t gpsd_utc_resolve(struct gps_device_t *);
extern void gpsd_century_update(struct gps_device_t *, int);

extern void gpsd_zero_satellites(struct gps_data_t *sp);

# ifdef __cplusplus
extern "C" {
# endif
# ifdef __cplusplus
}
# endif

// do not call gpsd_log() directly, use GPSD_LOG() to save a lot of cpu time
PRINTF_FUNC(3, 4) void gpsd_log(const int, const struct gpsd_errout_t *,
                                const char *, ...);

/*
 * GPSD_LOG() is the new one debug logger to rule them all.
 *
 * The calling convention is not attractive:
 *     GPSD_LOG(debuglevel, (fmt, ...));
 *     GPSD_LOG(2, ("this will appear on stdout if debug >= %d\n", 2));
 *
 * This saves significant pushing, popping, hexification, etc. when
 * the debug level does not require it.
 */

#if 1
#define GPSD_LOG(lvl, eo, ...)                 \
    do {                                       \
        if (unlikely((eo)->debug >= (lvl))) {  \
            gpsd_log(lvl, eo, __VA_ARGS__);    \
        }                                      \
    } while (0)
#else
#define GPSD_LOG(lvl, eo, ...)
//#define GPSD_LOG(lvl, eo, ...)  printf(__VA_ARGS__)

#endif



#define NITEMS(x) ((int) (sizeof(x) / sizeof(x[0]) + COMPILE_CHECK_IS_ARRAY(x)))

/*
 * C99 requires NAN to be defined if the implementation supports quiet
 * NANs.  At one point, it seems Solaris did not define NAN; it is not
 * clear if this is still true.
 */
#ifndef NAN
#define NAN (0.0f/0.0f)
#endif

# ifdef __cplusplus
}
# endif

#endif  // _GPSD_H_
// Local variables:
// mode: c
// end:
// vim: set expandtab shiftwidth=4
